<!-- 现展示页面
 bug：
 1.文字流失输出换行
 2.想一想消失过早
  -->


<template>
  <div style="height: calc(100vh - 100px)">
    <div class="talkContainer">
      <div class="talkContent" ref="chatContent">
        <div class="all">
          <div class="icon">
            <img src="@/assets/images/talk.png" alt="" />
          </div>
          <div class="aitalk" v-html="aitalk"></div>
        </div>
        <div class="talkQA" v-for="(item, index) in talkList" :key="item.id">
          <div v-if="item.person == 'mine'" class="mine">
            <img
              style="width: 100px; height: 100px"
              v-if="item.img"
              :src="item.img"
              alt=""
            />
            <div class="question">
              {{ item.say }}
            </div>
            <div class="icon">
              <img src="@/assets/images/mine.png" alt="" />
            </div>
          </div>
          <div
            v-if="item.person == 'ai'"
            class="all"
            style="margin-bottom: 10px"
          >
            <div class="icon">
              <img src="@/assets/images/talk.png" alt="" />
            </div>
            <div class="aitalk" v-html="item.say"></div>
          </div>
          <div v-if="item.person == 'ai' && item.isShow" class="assess">
            <img
              :src="
                item.isGood
                  ? require('@/assets/images/good-a.png')
                  : require('@/assets/images/good.png')
              "
              alt=""
              @click="getGood(item, index)"
            />
            <el-popover
              v-model="item.isPop"
              :ref="`popover-${index}`"
              placement="bottom"
              title="期待您的反馈"
              width="400"
              trigger="click"
              @hide="hideDiv(item, index)"
            >
              <el-checkbox-group v-model="checkboxGroup1">
                <el-checkbox
                  v-for="option in options"
                  :label="option"
                  :key="option"
                  border
                  >{{ option }}</el-checkbox
                >
              </el-checkbox-group>
              <el-input
                v-model="other"
                type="textarea"
                :autosize="{ minRows: 2, maxRows: 4 }"
                placeholder="其他"
              ></el-input>
              <div style="text-align: right; margin: 20px 0">
                <el-button @click="cancel(item, index)">取消</el-button>
                <el-button type="primary" @click="ok(item, index)"
                  >确认</el-button
                >
              </div>
              <img
                slot="reference"
                :src="
                  item.isBad
                    ? require('@/assets/images/bad-a.png')
                    : require('@/assets/images/bad.png')
                "
                alt=""
                @click="getBad(item, index)"
              />
            </el-popover>
          </div>
        </div>
      </div>
      <div class="talkInput">
        <!-- <div class="img-list">
          <img
            v-if="imgUrl"
            style="width: 50px; height: 50px"
            :src="imgUrl"
            alt=""
          />
        </div> -->
        <div class="select">
          <span>信号专业知识库</span>
          <!-- <el-select
            :value="kb_name"
            filterable
            placeholder="请选择知识库"
            style="width: 150px; border: none"
            disabled
          >
            <el-option
              key="1be049f5183647b081c7ea6dd21948b2"
              label="信号专业知识库"
              value="1be049f5183647b081c7ea6dd21948b2"
            >
            </el-option>
          </el-select> -->
        </div>
        <form @submit.prevent="getQuestion" class="userSearch">
          <div class="content">
            <div class="upload">
              <el-upload
                action="#"
                :limit="1"
                :file-list="fileList"
                :on-change="handleChange"
                :auto-upload="false"
                list-type="picture"
                accept=".jpg,.png"
                @keydown.enter.native="getQuestion"
              >
                <!-- <i
                  class="el-icon-picture"
                  style="font-size: 30px; color: #28c76c"
                ></i> -->
                <el-button size="small" style="padding: 0">
                  <i
                    class="el-icon-picture"
                    style="font-size: 30px; color: #28c76c"
                  ></i>
                </el-button>
              </el-upload>
            </div>
            <div class="input">
              <el-input
                v-model="contentVal"
                type="textarea"
                placeholder="请输入内容"
                style="resize: none"
                autosize
                :row="1"
                :maxlength="200"
                @keydown.enter.native="getQuestion"
                :disabled="isDisabled"
              ></el-input>
              <!-- isDisabled -->
            </div>
          </div>
        </form>
        <div class="send">
          <i
            class="el-icon-s-promotion"
            style="font-size: 30px; color: #28c76c; margin-right: 20px"
            @click="getQuestion"
          ></i>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import * as zskApi from "@/api/zsk";
import { marked } from "marked";
import markdown from "markdown-it";
export default {
  data() {
    return {
      talkList: [],
      contentVal: "",
      dataList: [],
      kb_name: "1be049f5183647b081c7ea6dd21948b2",
      loading: true,
      isSend: false,
      fileList: [],
      file: "",
      imgUrl: "",
      contentVal_say: "",
      aitalk: "",
      text: "",
      timer: null,
      isDisabled: false,
      user_id: JSON.parse(localStorage.getItem("userInfo")).id,
      options: [
        "答非所问",
        "回答有误",
        "格式问题",
        "没有帮助",
        "事实错误",
        "有害信息",
      ],
      checkboxGroup1: [],
      other: "",
      desc: "",
      bad: false,
      good: false,
    };
  },

  created() {
    this.getShow();
    this.getList();
  },

  updated() {
    this.scrollToBottom();
  },

  methods: {
    scrollToBottom() {
      this.$nextTick(() => {
        this.$refs.chatContent.scrollTop = this.$refs.chatContent.scrollHeight;
      });
    },
    selectItem(item) {
      this.kb_name = item.key;
    },
    getList() {
      zskApi
        .select("http://103.237.29.235:7866/v1/knowledge-base/select")
        .then((res) => {
          if (res.code == 1) {
            this.dataList = res.data;
          }
        });
    },
    async getShow() {
      let text = `
        <h1>🚄 你好，我是你的信号专业多模态大模型助手—POC测试</h1>
        <p>👋 我由商汤科技打造，接入了最新的知识库及多模态大模型，能够为你提供专业的工程领域知识参考，是你的工作好帮手～</p>
        <p>你可以在提问框旁选择对应的知识库，让我帮你准确地查询有关的专业知识。</p>
        <p>我有以下一些功能：</p>
        <p>🔍 基于特定的专业知识库，帮你检索对应的法规规范</p>
        <p>👀 上传专业图片，帮你解释图片内容</p>
        <p>❎ 在POC测试阶段，如果你问的问题和专业无关，我会拒绝你哦！</p>
        <br>
        <p>你可以尝试这样来问我：</p>
        <p>- 临时限速服务器（TSRS）与下列哪些系统接口？</p>
        <p>- 调度集中系统(CTC)具备哪两种作业办理模式？</p>
        <p>- 自动闭塞区间信号设备管辖分界要满足哪些要求？</p>
        
        `;
      let m = "";

      for (let i = 0; i < text.length; i++) {
        m += text.charAt(i);
        await new Promise((resolve) => {
          setTimeout(resolve, 30);
        });
        this.aitalk = m;
      }
    },

    handleChange(file, fileList) {
      this.file = file.raw;
      this.fileList = fileList;
      this.imgUrl = file.url;
    },
    changeZSK(val) {
      this.kb_name = val;
    },
    getQuestion(event) {
      event.preventDefault();
      if (this.fileList && this.fileList.length > 0) {
        if (this.contentVal) {
          this.talkList.push(
            {
              id: this.generateUniqueID(),
              person: "mine",
              img: this.imgUrl,
            },
            {
              id: this.generateUniqueID(),
              person: "mine",
              say: this.contentVal,
            },
            {
              id: this.generateUniqueID(),
              person: "ai",
              isGood: false,
              isBad: false,
              isShow: false,
              say: "让我想一想",
            }
          );
        } else {
          this.talkList.push(
            {
              id: this.generateUniqueID(),
              person: "mine",
              img: this.imgUrl,
            },
            {
              id: this.generateUniqueID(),
              person: "ai",
              isBad: false,
              isGood: false,
              isShow: false,
              say: "让我想一想",
            }
          );
        }
        this.isDisabled = true;
        this.getAnswer1();
        this.contentVal = "";
        this.fileList = [];
      } else {
        if (this.contentVal == "") {
          this.$message.warning("请输入提问内容");
        }
        this.talkList.push(
          {
            id: this.generateUniqueID(),
            person: "mine",
            say: this.contentVal,
          },
          {
            id: this.generateUniqueID(),
            person: "ai",
            isGood: false,
            isBad: false,
            say: "让我想一想",
          }
        );
        this.isDisabled = true;
        this.submitForm1();
        this.contentVal = "";
      }
    },
    async submitForm1() {
      let isUpdate = true;
      let lastObj = this.talkList[this.talkList.length - 1];

      lastObj.question = this.contentVal;
      this.timer = setInterval(() => {
        if (isUpdate) {
          if (lastObj.say.length >= 5) {
            lastObj.say += " .";
          }
          if (lastObj.say.length > 11) {
            lastObj.say = "让我想一想";
          }
        }
      }, 200);
      let that = this;
      var md = new markdown();
      let index = 0;
      let num=0;
      var str="";
      fetch("http://103.237.29.235:7866/chat/knowledge_base_chat", {
        headers: {
          "Content-Type": "application/json",
        },
        method: "POST",
        body: JSON.stringify({
          query: that.contentVal,
          session_id: "session_id",
          kb_name: this.kb_name,
          stream: true,
        }),
      })
        .then((response) => {
          lastObj.say = "";
          isUpdate = false;
          that.timer = null;
          if (!response.ok) {

            throw new Error("Network response was not ok");
          }

          return response.body.getReader();
        })
        .then((reader) => {
          const stream = new ReadableStream({
            start(controller) {
              function push() {
                reader
                  .read()
                  .then(async ({ done, value }) => {
                    let decoder = new TextDecoder();
                    let txt = decoder.decode(value);

                    if (done) {
                      index = 0;
                      console.log("已经获取到所有的流数据");
                      that.isDisabled = false;
                      that.good = false;
                      that.bad = false;
                      lastObj.isShow = true;
                      that.scrollToBottom();
                      controller.close();
                      return;
                    } else {
                      index++;
                      console.log("第" + index + "次流数据获取：");
                      console.log([txt]);
                        txt = md.render(txt);
                        txt = txt
                        .replace(/&lt;/g, "<")
                        .replace(/&gt;/g, ">")
                        .replace(/&quot;/g, '"');
                      for (let i = 0; i < txt.length; i++) {
                        lastObj.say += txt.charAt(i);
                        await new Promise((resolve) => setTimeout(resolve, 10));
                      }
                    }

                    controller.enqueue(value);
                    push();
                  })
                  .catch((err) => {
                    console.error("Stream reading error:", err);
                    controller.error(err);
                  });
              }
              push();
            },
          });

          return new Response(stream);
        })
        .then((response) => response.text())
        .then(async (data) => {
          // console.log(data); // 处理接收到的数据
          console.log("最后一次流数据展示：");
          data = md.render(data);
          data = data
            .replace(/&lt;/g, "<")
            .replace(/&gt;/g, ">")
            .replace(/&quot;/g, '"')
            .replace("\n", "<br/>");
          console.log([data]);
          lastObj.say = data;
        })
        .catch((error) => {
          console.error("Fetch error:", error);
        });
    },
    //多模态
    async getAnswer1() {
      let isUpdate = true;
      let lastObj = this.talkList[this.talkList.length - 1];

      lastObj.question = this.contentVal;
      //处理。。。
      this.timer = setInterval(() => {
        if (isUpdate) {
          if (lastObj.say.length >= 5) {
            lastObj.say += " .";
          }
          if (lastObj.say.length > 11) {
            lastObj.say = "让我想一想";
          }
        }
      }, 200);
      const formData = new FormData();
      formData.append("file", this.file);
      formData.append("query", this.contentVal);
      formData.append("kb_name", this.kb_name);
      formData.append("stream", true);
      let response = await fetch(
        "http://103.237.29.235:7866/chat/multimodal_chat",
        {
          method: "POST",
          body: formData,
        }
      )
        .then()
        .catch((error) => console.error("Fetch error:", error));
      const reader = response.body.getReader();
      const textDecoder = new TextDecoder();
      var md = new markdown();
      let answerClear = false;
      let str_lsit = "";
      let index = 0;
      while (true) {
        const { done, value } = await reader.read();
        if (done) {
          index = 0;
          console.log("数据已经获取完毕");
          this.isDisabled = false;
          this.good = false;
          this.bad = false;
          lastObj.isShow = true;
          break;
        }
        isUpdate = true;
        // isUpdate = false;
        // this.timer = null;

        if (!isUpdate && !answerClear) {
          answerClear = true;
          // lastObj.say = "";
        }
        let chunkText = textDecoder.decode(value);
        str_lsit += textDecoder.decode(value);
        index++;
        console.log("第" + index + "次流数据获取：");
        console.log([chunkText]);
        // let num =0;
        // console.log("第" + num + "次流数据获取：");
        //   chunkText = md.render(chunkText);
        // for (let i = 0; i < chunkText.length; i++) {
        //   lastObj.say += chunkText.charAt(i);
        //   await new Promise((resolve) => setTimeout(resolve, 10));
        // }
        }
        str_lsit = md.render(str_lsit);
        lastObj.say = "";
        isUpdate = false;
        this.timer = null;
      for (let i = 0; i < str_lsit.length; i++) {
          lastObj.say += str_lsit.charAt(i);
          await new Promise((resolve) => setTimeout(resolve, 10));
        }
      console.log([lastObj.say]);
    },

    //生成id

    generateUniqueID() {
      // 使用当前日期和时间以及随机数生成唯一ID
      const time = new Date().getTime();
      const random = Math.random().toString(36).substring(2, 15);
      return "id_" + time + "_" + random;
    },
    isValidMarkdown(str) {
      const markdownPattern =
        /(#+\s+.*)|(\*{1,5}\s+\S+\s+\*{1,5})|(>+\s+.*)|(\d+\.\s+.*)|(\*\s+.*)|(\[\!\[.*\]\(.*\))\((http.*)\)|(\[.*\]\(.*\))|(\!\[.*\]\((http.*)\))/g;
      return markdownPattern.test(str);
    },
    getBad(row, index) {
      this.bad = !this.bad;
      this.checkboxGroup1 = [];
      this.other = "";
      this.desc = "";
      if (this.talkList[index].isBad == false) {
        this.talkList[index].isBad = true;
        this.talkList[index].isGood = false;
      } else {
        this.talkList[index].isBad = false;
        this.talkList[index].isPop = true;
        this.$http
          .post("http://103.177.28.196:7866/feedback/cancel_vote", {
            user_id: this.user_id,
            feedback_id: this.talkList[index].feedback_id,
          })
          .then((res) => {
            let result = res.data;
            this.talkList[index].isBad = false;
            if (result.code == 200) {
              // this.$message.success("操作完成");
            }
          });
      }
    },
    getGood(row, index) {
      console.log(row);

      this.good = !this.good;

      let data = {
        user_id: this.user_id,
        query: row.question,
        answer: row.say,
        kb_name: this.kb_name,
        status: "upvote",
        desc: this.desc,
      };
      if (this.talkList[index].isGood == false) {
        this.$http
          .post("http://103.177.28.196:7866/feedback/vote", data)
          .then((res) => {
            let result = res.data;
            if (result.code == 200) {
              row.feedback_id = result.data.feedback_id;
              this.talkList[index].isGood = true;
              this.talkList[index].isBad = false;
            }
          });
      } else {
        this.$http
          .post("http://103.177.28.196:7866/feedback/cancel_vote", {
            user_id: this.user_id,
            feedback_id: row.feedback_id,
          })
          .then((res) => {
            let result = res.data;
            if (result.code == 200) {
              this.talkList[index].isGood = false;
            }
          });
      }
    },
    ok(row, index) {
      if (this.checkboxGroup1.length > 0 || this.other) {
        if (this.other) {
          this.checkboxGroup1.push(this.other);
        }
        this.desc = this.checkboxGroup1.join(",");

        if (this.desc && this.talkList[index].isBad == true) {
          this.pick(row, index);
          this.$refs[`popover-${index}`][0].showPopper = false;
        }
      } else {
        this.$message.info("请输入反馈意见");
      }
    },
    cancel(row, index) {
      this.$refs[`popover-${index}`][0].showPopper = false;
    },
    hideDiv(row, index) {
      if (this.desc == "" && this.talkList[index].isBad == true) {
        let data = {
          user_id: this.user_id,
          query: this.talkList[index].question,
          answer: this.talkList[index].say,
          kb_name: this.kb_name,
          status: "downvote",
          desc: this.desc,
        };
        this.$http
          .post("http://103.177.28.196:7866/feedback/vote", data)
          .then((res) => {
            let result = res.data;
            if (result.code == 200) {
              row.feedback_id = result.data.feedback_id;
            }
          });
      }
    },

    pick(row, index) {
      let data = {
        user_id: this.user_id,
        query: this.talkList[index].question,
        answer: this.talkList[index].say,
        kb_name: this.kb_name,
        status: "downvote",
        desc: this.desc,
      };

      this.$http
        .post("http://103.177.28.196:7866/feedback/vote", data)
        .then((res) => {
          let result = res.data;
          if (result.code == 200) {
            this.talkList[index].feedback_id = result.data.feedback_id;
            this.$message.success("感谢您的反馈");
          }
        });
    },
  },
};
</script>

<style scoped>
.talkContainer {
  width: 80%;
  margin: 10px auto;
  display: flex;
  flex-direction: column;
  height: 100%;
}

.talkContent {
  width: 100%;
  flex: 1;
  overflow: auto;
}

.talkContent::-webkit-scrollbar {
  display: none; /* 针对Webkit浏览器隐藏滚动条 */
}
.talkInput {
  width: 100%;
  margin: 30px 0;
  background-color: #fff;
  border-radius: 15px;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
  padding: 10px;
}

.img-list {
  display: flex;
  align-items: center;
  justify-content: start;
}

.all {
  display: flex;
  justify-content: start;
  align-items: center;
}

.icon img {
  width: 20px;
  height: 20px;
}

.aitalk {
  /* width: 50%; */
  margin-left: 10px;
  padding: 8px 10px 12px;
  background-color: #fff;
  border-radius: 15px;
}
.userSearch {
  display: flex;
  flex: 1;
}
.content {
  width: 100%;
  display: flex;
  justify-content: start;
  align-items: center;
}

.el-textarea /deep/ .el-textarea__inner {
  resize: none;
  border: none;
  /* line-height: 70px; */
  /* border: 1px solid red; */
}
.input {
  flex: 1;
  display: flex;
  align-items: center;
}
.el-textarea {
  /* border: 1px solid red; */
  display: flex;
  align-items: center;
  font-size: 20px !important;
  font-weight: 600;
  /* height: 70px !important; */
  /* line-height: 70px; */
}
.select,
.send,
.upload {
  margin: 0 10px;
}

.mine {
  display: flex;
  justify-content: end;
  align-items: center;
  padding: 15px 0;
  margin: 20px 0;
}
.ai {
  display: flex;
  justify-content: start;
  align-items: center;
  background-color: #fff;
  padding: 15px 0;
}

.question {
  margin-right: 10px;
  font-weight: 600;
}
.upload div {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 0 10px;
}
.upload /deep/ .el-upload-list__item-name {
  display: none;
}
.upload /deep/.el-upload-list__item-thumbnail {
  width: 50px;
  height: 50px;
  position: absolute;
  margin-left: 0px;
}
.upload /deep/ .el-upload-list__item {
  width: 80px;
  height: 80px;
  padding: 15px 0 0 13px;
}

.upload /deep/ .el-icon-close {
  right: 0;
}

.upload /deep/ .el-upload-list {
  /* position: absolute;
  top: 0;
  left: 0;*/
}
.assess {
  margin-left: 20px;
}
.assess img {
  width: 20px;
  height: 20px;
  margin: 1px 10px;
}
.el-popover .el-button {
  border: 1px solid #dcdfe6;
}
.el-popover .el-textarea {
  border: 1px solid #dcdfe6;
  margin: 10px;
  width: 360px;
}
.el-checkbox /deep/ .el-checkbox__input {
  display: none;
}
.el-checkbox {
  margin: 10px;
}
</style>